{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "c13154bd-70d8-4185-acba-b033a16bc895",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "3cabaa26-5f23-4c70-a88b-7774ed66a822",
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.preprocessing import MinMaxScaler\n",
    "from sklearn.preprocessing import StandardScaler"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d73eb76-cbe6-4fc1-ade9-df5979dab713",
   "metadata": {},
   "source": [
    "## 手写梯度下降实现线性回归房价估值"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28333b81-1213-4405-ad95-4ecc986e7791",
   "metadata": {},
   "source": [
    "## 一、导入数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e392ce13-e15e-460e-ad9c-06233da123fe",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>X1 transaction date</th>\n",
       "      <th>X2 house age</th>\n",
       "      <th>X3 distance to the nearest MRT station</th>\n",
       "      <th>X4 number of convenience stores</th>\n",
       "      <th>X5 latitude</th>\n",
       "      <th>X6 longitude</th>\n",
       "      <th>Y house price of unit area</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>No</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2012.916667</td>\n",
       "      <td>32.0</td>\n",
       "      <td>84.87882</td>\n",
       "      <td>10</td>\n",
       "      <td>24.98298</td>\n",
       "      <td>121.54024</td>\n",
       "      <td>37.9</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>2012.916667</td>\n",
       "      <td>19.5</td>\n",
       "      <td>306.59470</td>\n",
       "      <td>9</td>\n",
       "      <td>24.98034</td>\n",
       "      <td>121.53951</td>\n",
       "      <td>42.2</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>2013.583333</td>\n",
       "      <td>13.3</td>\n",
       "      <td>561.98450</td>\n",
       "      <td>5</td>\n",
       "      <td>24.98746</td>\n",
       "      <td>121.54391</td>\n",
       "      <td>47.3</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>2013.500000</td>\n",
       "      <td>13.3</td>\n",
       "      <td>561.98450</td>\n",
       "      <td>5</td>\n",
       "      <td>24.98746</td>\n",
       "      <td>121.54391</td>\n",
       "      <td>54.8</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>2012.833333</td>\n",
       "      <td>5.0</td>\n",
       "      <td>390.56840</td>\n",
       "      <td>5</td>\n",
       "      <td>24.97937</td>\n",
       "      <td>121.54245</td>\n",
       "      <td>43.1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "    X1 transaction date  X2 house age  X3 distance to the nearest MRT station  \\\n",
       "No                                                                              \n",
       "1           2012.916667          32.0                                84.87882   \n",
       "2           2012.916667          19.5                               306.59470   \n",
       "3           2013.583333          13.3                               561.98450   \n",
       "4           2013.500000          13.3                               561.98450   \n",
       "5           2012.833333           5.0                               390.56840   \n",
       "\n",
       "    X4 number of convenience stores  X5 latitude  X6 longitude  \\\n",
       "No                                                               \n",
       "1                                10     24.98298     121.54024   \n",
       "2                                 9     24.98034     121.53951   \n",
       "3                                 5     24.98746     121.54391   \n",
       "4                                 5     24.98746     121.54391   \n",
       "5                                 5     24.97937     121.54245   \n",
       "\n",
       "    Y house price of unit area  \n",
       "No                              \n",
       "1                         37.9  \n",
       "2                         42.2  \n",
       "3                         47.3  \n",
       "4                         54.8  \n",
       "5                         43.1  "
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "house_df = pd.read_excel('../dataset/Real estate valuation data set.xlsx', index_col='No')\n",
    "house_df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "272630a8-d6dc-4984-9d28-a50f2a45f726",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "Int64Index: 414 entries, 1 to 414\n",
      "Data columns (total 7 columns):\n",
      " #   Column                                  Non-Null Count  Dtype  \n",
      "---  ------                                  --------------  -----  \n",
      " 0   X1 transaction date                     414 non-null    float64\n",
      " 1   X2 house age                            414 non-null    float64\n",
      " 2   X3 distance to the nearest MRT station  414 non-null    float64\n",
      " 3   X4 number of convenience stores         414 non-null    int64  \n",
      " 4   X5 latitude                             414 non-null    float64\n",
      " 5   X6 longitude                            414 non-null    float64\n",
      " 6   Y house price of unit area              414 non-null    float64\n",
      "dtypes: float64(6), int64(1)\n",
      "memory usage: 25.9 KB\n"
     ]
    }
   ],
   "source": [
    "house_df.info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "b2e153f7-6cf8-4ca9-810f-830e68de3384",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>X1 transaction date</th>\n",
       "      <th>X2 house age</th>\n",
       "      <th>X3 distance to the nearest MRT station</th>\n",
       "      <th>X4 number of convenience stores</th>\n",
       "      <th>X5 latitude</th>\n",
       "      <th>X6 longitude</th>\n",
       "      <th>Y house price of unit area</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>count</th>\n",
       "      <td>414.000000</td>\n",
       "      <td>414.000000</td>\n",
       "      <td>414.000000</td>\n",
       "      <td>414.000000</td>\n",
       "      <td>414.000000</td>\n",
       "      <td>414.000000</td>\n",
       "      <td>414.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>mean</th>\n",
       "      <td>2013.148953</td>\n",
       "      <td>17.712560</td>\n",
       "      <td>1083.885689</td>\n",
       "      <td>4.094203</td>\n",
       "      <td>24.969030</td>\n",
       "      <td>121.533361</td>\n",
       "      <td>37.980193</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>std</th>\n",
       "      <td>0.281995</td>\n",
       "      <td>11.392485</td>\n",
       "      <td>1262.109595</td>\n",
       "      <td>2.945562</td>\n",
       "      <td>0.012410</td>\n",
       "      <td>0.015347</td>\n",
       "      <td>13.606488</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>min</th>\n",
       "      <td>2012.666667</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>23.382840</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>24.932070</td>\n",
       "      <td>121.473530</td>\n",
       "      <td>7.600000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>25%</th>\n",
       "      <td>2012.916667</td>\n",
       "      <td>9.025000</td>\n",
       "      <td>289.324800</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>24.963000</td>\n",
       "      <td>121.528085</td>\n",
       "      <td>27.700000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>50%</th>\n",
       "      <td>2013.166667</td>\n",
       "      <td>16.100000</td>\n",
       "      <td>492.231300</td>\n",
       "      <td>4.000000</td>\n",
       "      <td>24.971100</td>\n",
       "      <td>121.538630</td>\n",
       "      <td>38.450000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>75%</th>\n",
       "      <td>2013.416667</td>\n",
       "      <td>28.150000</td>\n",
       "      <td>1454.279000</td>\n",
       "      <td>6.000000</td>\n",
       "      <td>24.977455</td>\n",
       "      <td>121.543305</td>\n",
       "      <td>46.600000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>max</th>\n",
       "      <td>2013.583333</td>\n",
       "      <td>43.800000</td>\n",
       "      <td>6488.021000</td>\n",
       "      <td>10.000000</td>\n",
       "      <td>25.014590</td>\n",
       "      <td>121.566270</td>\n",
       "      <td>117.500000</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       X1 transaction date  X2 house age  \\\n",
       "count           414.000000    414.000000   \n",
       "mean           2013.148953     17.712560   \n",
       "std               0.281995     11.392485   \n",
       "min            2012.666667      0.000000   \n",
       "25%            2012.916667      9.025000   \n",
       "50%            2013.166667     16.100000   \n",
       "75%            2013.416667     28.150000   \n",
       "max            2013.583333     43.800000   \n",
       "\n",
       "       X3 distance to the nearest MRT station  \\\n",
       "count                              414.000000   \n",
       "mean                              1083.885689   \n",
       "std                               1262.109595   \n",
       "min                                 23.382840   \n",
       "25%                                289.324800   \n",
       "50%                                492.231300   \n",
       "75%                               1454.279000   \n",
       "max                               6488.021000   \n",
       "\n",
       "       X4 number of convenience stores  X5 latitude  X6 longitude  \\\n",
       "count                       414.000000   414.000000    414.000000   \n",
       "mean                          4.094203    24.969030    121.533361   \n",
       "std                           2.945562     0.012410      0.015347   \n",
       "min                           0.000000    24.932070    121.473530   \n",
       "25%                           1.000000    24.963000    121.528085   \n",
       "50%                           4.000000    24.971100    121.538630   \n",
       "75%                           6.000000    24.977455    121.543305   \n",
       "max                          10.000000    25.014590    121.566270   \n",
       "\n",
       "       Y house price of unit area  \n",
       "count                  414.000000  \n",
       "mean                    37.980193  \n",
       "std                     13.606488  \n",
       "min                      7.600000  \n",
       "25%                     27.700000  \n",
       "50%                     38.450000  \n",
       "75%                     46.600000  \n",
       "max                    117.500000  "
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "house_df.describe()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "6adee9c3-79d1-465c-992f-fc6ca027d047",
   "metadata": {},
   "outputs": [],
   "source": [
    "data = house_df.iloc[:, :-1].values\n",
    "target = house_df.iloc[:, -1].values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15d7dbd0-5f16-4a94-a409-549a8c4bb514",
   "metadata": {},
   "source": [
    "## 二、划分数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "aa8ace6d-bfe4-4a5e-94f3-68843389432a",
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=44, random_state=10)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10ac5173-6ea1-4d6e-8603-53ba6f37ac89",
   "metadata": {},
   "source": [
    "## 三、数据预处理（归一化+标准化）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "cac26f27-cbb7-48bc-9feb-0ccf58f42247",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 选择一种缩放方式，例如使用MinMaxScaler\n",
    "mm_scaler = MinMaxScaler()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "9c70d633-96bc-4a1e-9849-b40c07ec82d6",
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train_scaled = mm_scaler.fit_transform(X_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "e881c1fe-6111-45d4-abad-20ae4d0ecd16",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 注意这里使用transform而非fit_transform，避免测试集被训练数据影响\n",
    "X_test_scaled = mm_scaler.transform(X_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e322482-4220-491c-885f-120cbd44022f",
   "metadata": {},
   "source": [
    "## 四、随机梯度下降"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "e9af0a02-38d6-41de-bc36-278a8e75fa80",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义计算梯度的函数\n",
    "def compute_gradient(X, y, w):\n",
    "    return 2 * X.T.dot(X.dot(w) - y) / y.size"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "148f55a4-751f-439d-972d-c625b81c2ede",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义SGD函数\n",
    "def sgd_linear_regression(X_train, y_train, alpha=0.01, max_iters=1000):\n",
    "    # 初始化权重，包括偏置项，大小为特征数加一\n",
    "    n_samples, n_features = X_train.shape\n",
    "    w = np.zeros(n_features + 1)  # 增加一个偏置项 6+1=7\n",
    "    # 添加偏置项，通常为1\n",
    "    X_train_b = np.concatenate((np.ones(shape=(n_samples, 1)), X_train_scaled), axis=1)\n",
    "    # 迭代\n",
    "    for _ in range(max_iters):\n",
    "        # 对每一个样本进行更新\n",
    "        for i in range(n_samples):\n",
    "            # SGD中的单样本梯度计算和更新\n",
    "            gradient = compute_gradient(X_train_b[i: i+1], y_train[i: i+1], w)\n",
    "            w -= alpha*gradient\n",
    "    return w[:-1], w[-1]  # 返回没有偏置项的权重和偏置项本身"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "3375a60f-01c8-43f0-8085-986e492cb545",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Learned weights: [ 30.80182918   5.33456674 -10.97846465 -26.23564587  11.15979652\n",
      "  19.6330427 ]\n",
      "Bias term: 0.835542415485262\n"
     ]
    }
   ],
   "source": [
    "weights, bias = sgd_linear_regression(X_train_scaled, y_train)\n",
    "print(\"Learned weights:\", weights)\n",
    "print(\"Bias term:\", bias)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2315e48a-4e0c-458c-b9ef-d38ce8381eee",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
